﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace JasLib.Interop.Win32
{
    /// <summary>
    /// Pointer-sized signed integer.
    /// </summary>
    /// <remarks>
    /// This is a perfectly capable substitute for the LONG_PTR and LPARAM types,
    /// and for the completely underwhelming System.IntPtr framework type.
    /// </remarks>
    [StructLayout(LayoutKind.Explicit)]
    public struct INT_PTR
    {
        public static readonly INT_PTR Zero = new INT_PTR(0);
        public static readonly int Size = MarshalHelper.SizeOf<INT_PTR>();
        public static readonly int Bits = Size * 8;
        public static readonly int HighestBitIndex = Bits - 1;

        static INT_PTR()
        {
            Debug.Assert(Size == IntPtr.Size, "INT_PTR absolutely MUST be the same size as a pointer.");
            Debug.Assert(Size <= 8, "INT_PTR isn't supported for pointer sizes larger than 64 bits.");
            return;
        }

        [FieldOffset(0)]
        internal unsafe void* _value;

        public unsafe INT_PTR(void* value)
        {
            _value = value;
        }

        public INT_PTR(uint iValue)
        {
            unsafe
            {
                _value = (void*)iValue;
            }
        }

        public INT_PTR(long lValue)
        {
            unsafe
            {
                _value = (void*)lValue;
            }
        }

        public override int GetHashCode()
        {
            unsafe
            {
                return (int)((ulong)this._value);
            }
        }

        public override bool Equals(object obj)
        {
            unsafe
            {
                return (obj is INT_PTR) && ((INT_PTR)obj)._value == _value;
            }
        }

        public override string ToString()
        {
            unsafe
            {
                return ((long)_value).ToString();
            }
        }

        public string ToString(string strFormat)
        {
            unsafe
            {
                return ((long)_value).ToString(strFormat);
            }
        }

        #region Typecast Operators

        public static implicit operator INT_PTR(int iValue)
        {
            unsafe
            {
                return new INT_PTR(iValue);
            }
        }

        public static implicit operator INT_PTR(long lValue)
        {
            unsafe
            {
                return new INT_PTR(lValue);
            }
        }

        public static implicit operator long(INT_PTR op1)
        {
            unsafe
            {
                return (long)op1._value;
            }
        }

        public unsafe static implicit operator INT_PTR(void* pValue)
        {
            return new INT_PTR { _value = pValue };
        }

        public static implicit operator UINT_PTR(INT_PTR op1)
        {
            unsafe
            {
                return new UINT_PTR { _value = op1._value };
            }
        }

        public unsafe static implicit operator UIntPtr(INT_PTR op1)
        {
            return new UIntPtr(op1._value);
        }

        public unsafe static implicit operator IntPtr(INT_PTR op1)
        {
            return new IntPtr(op1._value);
        }

        public static implicit operator INT_PTR(IntPtr op1)
        {
            unsafe
            {
                return new INT_PTR { _value = op1.ToPointer() };
            }
        }

        #endregion

        #region Arithmetic Operators

        public static INT_PTR operator +(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return new INT_PTR((long)op1._value + (long)op2._value);
            }
        }

        public static INT_PTR operator ++(INT_PTR op1)
        {
            unsafe
            {
                return new INT_PTR((long)op1._value + 1L);
            }
        }

        public static INT_PTR operator -(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return new INT_PTR((long)op1._value - (long)op2._value);
            }
        }

        public static INT_PTR operator --(INT_PTR op1)
        {
            unsafe
            {
                return new INT_PTR { _value = (void*)((long)op1._value - 1L) };
            }
        }

        public static INT_PTR operator *(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return new INT_PTR { _value = (void*)((long)op1._value * (long)op2._value) };
            }
        }

        public static INT_PTR operator /(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return new INT_PTR { _value = (void*)((long)op1._value / (long)op2._value) };
            }
        }

        public static INT_PTR operator %(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return new INT_PTR { _value = (void*)((long)op1._value % (long)op2._value) };
            }
        }

        public static bool operator >(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return (long)op1._value > (long)op2._value;
            }
        }

        public static bool operator >=(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return (long)op1._value >= (long)op2._value;
            }
        }

        public static bool operator <(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return (long)op1._value < (long)op2._value;
            }
        }

        public static bool operator <=(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return (long)op1._value <= (long)op2._value;
            }
        }

        public static bool operator ==(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return op1._value == op2._value;
            }
        }

        public static bool operator !=(INT_PTR op1, INT_PTR op2)
        {
            unsafe
            {
                return op1._value != op2._value;
            }
        }

        #endregion
    }
}
